package com.cares.common.dao.impl;

import java.sql.CallableStatement;
import java.sql.SQLException;
import java.sql.Types;
import java.util.Arrays;
import java.util.Date;

import javax.annotation.Resource;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.hibernate.HibernateException;
import org.hibernate.Session;
import org.springframework.dao.DataAccessException;
import org.springframework.orm.hibernate3.HibernateCallback;

import cn.sh.cares.dao.DaoException;

import com.cares.common.dao.JsflowDao;
import com.cares.common.dao.orm.hibernate.HibernateOrmExecution;
import com.cares.event.entity.TEventLog;


/**
 * 
 * @author xuji
 * dao基类
 */
public class JsflowDaoImpl implements JsflowDao{
	
	 private Log logger = LogFactory.getLog(getClass());

	/**
	 * hibernate实体操作类的注入
	 */
	@Resource(name = "hibernateEntityManager", type = HibernateOrmExecution.class)
	private HibernateOrmExecution entityManager;
	
	protected HibernateOrmExecution getEntityManager(){
		return entityManager;
	}
	
	/**
     * 调用无返回值的存储过程
     * jsflowParams: 工作流信息，依次为：流程实例ID，流程实例名称，任务节点id，任务节点名称
     * proName：存储过程名称，params：存储过程IN参数；可为空
     **/
    public void callProcedure(String[] jsflowParams, final String proName, final Object... inParams) throws DaoException{
	    if (logger.isInfoEnabled()) {
		    logger.info("Execute procedure[" + proName + "], params[" + Arrays.asList(inParams) + "]");
		}
	    
		try {
			entityManager.getHibernateTemplate().execute(
				new HibernateCallback() {
					@SuppressWarnings("deprecation")
					public Object doInHibernate(Session session) throws HibernateException, SQLException {
						//存储过程名称
						String callProcedure = "{call " +proName;
						if(null == inParams ){
							callProcedure = callProcedure + "()}";
						}else{
							callProcedure = callProcedure + "(";
							for(int i=0; i<inParams.length; i++){
								callProcedure = callProcedure + "?,";
							}
							if(callProcedure.endsWith(",")){
								callProcedure = callProcedure.substring(0, callProcedure.length()-1);
							}
							callProcedure = callProcedure + ")}";
						}
						
                        CallableStatement cs = session.connection().prepareCall(callProcedure);
                        if(inParams != null){  
        					int idx = 1;  
        					for(Object obj : inParams) {  
        						if(obj != null) {  
        							cs.setObject(idx, obj);  
        						}else {  
        							cs.setNull(idx, Types.NULL);  
        						}  
        						idx++;  
        					}  
                        }
                        cs.execute();
                        //return cs.getString("p_result");
                        return null;
					}
				});
			//记录存储过程调用成功信息
			saveProcedureLog(generateProcedureLog(jsflowParams, proName, null));
		} catch (DataAccessException dae) {
			saveProcedureLog(generateProcedureLog(jsflowParams, proName, dae));
		    throw new DaoException("Execute procedure[" + proName + "], params["  + "] error.", dae);
		} catch (RuntimeException e) {
			saveProcedureLog(generateProcedureLog(jsflowParams, proName, e));
		    throw new DaoException("Execute procedure[" + proName + "], params[" + "] error.", e);
		}
    }
    
    /**
     * 调用有返回值的存储过程
     * jsflowParams: 工作流信息，依次为：流程实例ID，流程实例名称，任务节点id，任务节点名称
     * proName：存储过程名称，params：存储过程IN参数；可为空
     * outParams为存储过程的返回参数名称
     * 返回对象由具体调用的存储过程决定，如果存储过程返回值只有一个，则返回一个object对象，如果有多个则返回object对象数组
     **/
    @SuppressWarnings("unchecked")
	@Override
	public <T> T callProceReturnValue(String[] jsflowParams, final String proName,
			final Object[] inParams, final String... outParams) throws DaoException {
    	if (logger.isInfoEnabled()) {
		    logger.info("Execute procedure[" + proName + "], params[" + Arrays.asList(inParams) + "].");
		}
    	
    	try {
    		T result = (T)entityManager.getHibernateTemplate().execute(
				new HibernateCallback() {
					@SuppressWarnings("deprecation")
					public Object doInHibernate(Session session) throws HibernateException, SQLException {
						//存储过程名称
						String callProcedure = "{call " +proName + "(";
						if(null != inParams ){
							for(int i=0; i<inParams.length; i++){
								callProcedure = callProcedure + "?,";
							}
						}
						if(null!=outParams){
							for(int i=0; i<outParams.length; i++){
								callProcedure = callProcedure + "?,";
							}
						}
						if(callProcedure.endsWith(",")){
							callProcedure = callProcedure.substring(0, callProcedure.length()-1);
						}
						callProcedure = callProcedure + ")}";
						
                        CallableStatement cs = session.connection().prepareCall(callProcedure);
                        //添加IN 参数
                        if(inParams != null){  
        					int idx = 1;  
        					for(Object obj : inParams) {  
        						if(obj != null) {  
        							cs.setObject(idx, obj);  
        						}else {  
        							cs.setNull(idx, Types.NULL);  
        						}  
        						idx++;  
        					}  
                        }
                        //添加OUT参数名称
                        if(null!=outParams){
                        	for(String str : outParams){
                        		cs.registerOutParameter(str, Types.VARCHAR);
                        	}
                        }
                        cs.execute();
                        //返回单个对象，或者数组
                    	if(1==outParams.length){
                    		return cs.getString(outParams[0]);
                    	}else{
                    		Object[] results = new Object[outParams.length];
                    		for(int i=0; i<outParams.length; i++){
                    			results[i] = cs.getString(outParams[i]);
                    		}
                    		return results;
                    	}
					}
				});
    		//记录存储过程调用成功信息
    		saveProcedureLog(generateProcedureLog(jsflowParams, proName, null));
    		return result;
		} catch (DataAccessException dae) {
			saveProcedureLog(generateProcedureLog(jsflowParams, proName, dae));
			logger.error(getErrorMsg(proName,dae)); 
		    throw new DaoException("Execute procedure[" + proName + "], params["  + "] error.", dae);
		} catch (RuntimeException e) {
			saveProcedureLog(generateProcedureLog(jsflowParams, proName, e));
			logger.error(getErrorMsg(proName, e)); 
		    throw new DaoException("Execute procedure[" + proName + "], params[" + "] error.", e);
		}
    }    
    
    /**
     * 异常信息转换字符串工具方法
     */
    private String getErrorMsg(String proName, Exception e){
    	StackTraceElement [] messages = e.getStackTrace();
	    int length = messages.length;
	    String errorMsg = proName;
	    if(proName.length()<45){//存储过程名称不足45个字符，则用空格填充
	    	String temp = "";
	    	for(int i=0; i<45-proName.length(); i++){
	    		temp += " ";
	    	}
	    	errorMsg = errorMsg + temp + "\n";
	    }
	    for(int i=0; i<length; i++){
		    errorMsg += messages[i].toString() + "\n";
	    }
	    return errorMsg;
    }
	
    /**
     * 
     * @param jsflowParams 工作流信息
     * @param proName 存储过程名称
     * @param e 异常信息，为null时表示 执行无异常
     * @return
     */
    private TEventLog generateProcedureLog(String[] jsflowParams, String proName, Exception e){
    	TEventLog flowLog = new TEventLog();
    	flowLog.setClassName(this.getClass().getName());
    	flowLog.setCallName(proName);
    	flowLog.setCallType("PROCEDURE");
    	flowLog.setExcuteTime(new Date());
    	flowLog.setFlowExcuteId(jsflowParams[0]);
    	flowLog.setFlowName(jsflowParams[1]);
    	flowLog.setFlowTaskId(jsflowParams[2]);
    	flowLog.setFlowTaskName(jsflowParams[3]);
    	
    	if(null==e){
    		flowLog.setInfolevel("success");
    		flowLog.setMessage("success");
    	}else{
    		flowLog.setInfolevel("error");
    		StackTraceElement [] messages = e.getStackTrace();
    	    int length = messages.length;
    	    String errorMsg = "";
    	    for(int i=0; i<length; i++){
    		    errorMsg += messages[i].toString() + "\n";
    	    }
        	flowLog.setMessage(errorMsg);
    	}
    	
    	return flowLog;
    	
    }
    
    /***
     * 存储日志信息 
     */
   public void saveProcedureLog(TEventLog flowLog){
	   entityManager.save(flowLog);
    }
}

